﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.Nist;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.X509;

namespace TheClerk.Forms.Templates.raw.Envelope
{
    #region internal StrongBoxItem
    /// <summary>
    /// A copy of the symmetric key used to unlock OpaquePayload
    /// </summary>
    /// <remarks>
    /// StrongBoxItem ::= SEQUENCE {
    ///     asymmetricAlgorithm AlgorithmIdentifier,
    ///     destinationKeyIdentifier OCTET STRING OPTIONAL,
    /// -- the encrypted symmetric key
    ///     strongItem OCTET STRING }
    /// </remarks>
    internal class StrongBoxItem
    {
        // AsymmetricAlgorithm asymmetricAlgorithm;  // this is part of AsymmetricIdentity
        AsymmetricIdentity recipient;
        private byte[] symmetricSecret;
        private Chop OuterChop;

        // TODO: change to provide the Symmetric algorithm ID
        internal StrongBoxItem(AsymmetricIdentity recipient=null, byte[] symmetricSecret = null, Chop me = null)
        {
            // TODO: Complete member initialization
            this.recipient = recipient;
            this.symmetricSecret = (byte[])symmetricSecret.Clone();
            this.OuterChop = me;
        }

        internal Stream Serialize()
        {
            if (recipient != null && symmetricSecret == null)
            {
                throw new InvalidOperationException("strongboxitem has recipient but no secret");
            }

            var outputstream = new MemoryStream();
            {
                var dsg = new DerSequenceGenerator(outputstream);
                // TODO: Make this selectable
                dsg.AddObject(NistObjectIdentifiers.IdAes192Cbc);
                // The recipient public key is the best identity for itself
                // This doesn't mean that new identifier formats can't be created
                dsg.AddObject(new DerOctetString(SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(recipient.publicKey)));

                {
                    var agreement = new Org.BouncyCastle.Crypto.Agreement.ECDHWithKdfBasicAgreement("EC",
                        new Org.BouncyCastle.Crypto.Agreement.Kdf.ECDHKekGenerator(new Org.BouncyCastle.Crypto.Digests.Sha512Digest()));
                    agreement.Init(OuterChop.PrivateKey);
                    byte[] DHagreedkey;
                    byte[] cryptoutput;
                    DHagreedkey = agreement.CalculateAgreement(recipient.publicKey).ToByteArrayUnsigned();
                    var aese = new Org.BouncyCastle.Crypto.Engines.AesEngine();
                    var aesparam = new Org.BouncyCastle.Crypto.Parameters.KeyParameter(DHagreedkey,0,192/8);
                    var pbbc = new Org.BouncyCastle.Crypto.Paddings.PaddedBufferedBlockCipher(aese);
                    pbbc.Init(true, aesparam);
                    cryptoutput = new byte[pbbc.GetOutputSize(symmetricSecret.Length)];
                    pbbc.DoFinal(cryptoutput, 0, cryptoutput.Length);
                    dsg.AddObject(new DerOctetString(cryptoutput));
                    dsg.Close();
                }
            }
            outputstream.Seek(0, SeekOrigin.Begin);
            return outputstream;
        }

        internal Asn1Encodable toAsn1Encodable()
        {
            return Asn1Sequence.FromStream(Serialize());
        }


        internal void SetSymmetricKey(byte[] p)
        {
            symmetricSecret = p;
        }

        internal void SetOuterChop(Chop chop)
        {
            OuterChop = chop;
        }
    }
    #endregion
}
